package com.push.web.utils;

import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.*;

@Component
public class OnlineToolsRunPython {
    private static final String OS = System.getProperty("os.name");
    private static final String WINDOWS_PATH = "";
    private static final String LINUX_PATH = "";
    private static ExecutorService taskPool = new ThreadPoolExecutor(8, 16, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(600), new ThreadFactoryBuilder().setNameFormat("thread-自定义线程名-runner-%d").build());
    public static void execPythonFile(String fileName, String params){
        taskPool.submit(()->{
            try{
                exec(fileName,params);
            } catch(IOException e){
                System.out.println("读取python文件 fileName=" + fileName + " 异常：" + e.getMessage());
            }
                }
        );
    }
    public static void exec(String fileName,String params) throws IOException {
        Process process;
        if(OS.startsWith("Windows")){
            process = new ProcessBuilder("cmd.exe" , "/c","python",WINDOWS_PATH + fileName,params).start();

        } else {
            process = new ProcessBuilder("python3",LINUX_PATH + fileName, params).start();
        }

        new Thread(() -> {
            BufferedReader errorReader = null;
            errorReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> errorString = read(fileName,errorReader);
        }).start();
        new Thread(() -> {
            BufferedReader inputReader = null;
            inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> returnString = read(fileName, inputReader);
        }).start();
        try{
            process.waitFor();
        }catch(InterruptedException e){
            System.out.println("读取python文件 fileName=" + fileName + " " + fileName);
        }
    }
    private static List<String> read(String fileName, BufferedReader reader) {
        List<String> resultList = Lists.newArrayList();
        String res = "";
        while (true) {
            try{
                if(!((res = reader.readLine())!=null)) break;
            }catch (IOException e){
                System.out.println("读取python文件 fileName" + "" + fileName + " " + "读取结果异常" + e.getMessage());
            }
            resultList.add(res);
        }
        return resultList;
    }
    /**
     * 执行python文件 【同步 会等待py执行完毕】
     *
     * @param fileName python文件地址
     * @param params   参数
     * @throws IOException
     */
    public static String execPythonFileSync(String fileName, String params) {
        String result = "";
        try {
            result = execSync(fileName, params);
            return result ;
        } catch (Exception e) {
            // System.out.println("读取python文件 fileName=" + fileName + " 异常:" + e.getMessage());
            result = "error:" + e.getMessage();
        }
        return result;
    }
    private static String execSync(String fileName, String params) throws IOException {
        //System.out.println("同步读取python文件 init fileName={}&path={}" + " " + fileName + " " + WINDOWS_PATH);
        Process process;

        if (OS.startsWith("Windows")) {
            // windows执行脚本需要使用 cmd.exe /c 才能正确执行脚本
            process = new ProcessBuilder("cmd.exe", "/c", "python", WINDOWS_PATH + fileName, params).start();
        } else {
            // linux执行脚本一般是使用python3 + 文件所在路径
            process = new ProcessBuilder("python3", LINUX_PATH + fileName, params).start();
        }

        Future<Object> errFuture = taskPool.submit(() -> {
            //System.out.println("读取python文件 开始 fileName={}" + " " + fileName);
            BufferedReader errReader = null;
            // 脚本执行异常时的输出信息
            errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errStringList = read(fileName, errReader);
            //System.out.println("读取python文件 异常 fileName={}&errorString={}" + " " + fileName + " " + errorString);
            if(errStringList.size()>0){
                return errStringList.get(0);
            }else{
                return "";
            }
        });

        Future<Object> okFuture = taskPool.submit(() -> {
            // 脚本执行正常时的输出信息
            BufferedReader okReader = null;
            okReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> okStringList = read(fileName, okReader);
            //System.out.println("读取python文件 fileName={}&returnString={}" + " " + fileName + " " + returnString);
            if(okStringList.size()>0){
                return okStringList.get(0);
            }else{
                return "";
            }
        });

        try {
            //System.out.println("同步读取python文件 wait fileName={}" + " " + fileName);
            process.waitFor();
        } catch (InterruptedException e) {
            //System.out.println("同步读取python文件 fileName=" + " " + fileName + " " + " 等待结果返回异常" + " " + e.getMessage());
        }
        //System.out.println("同步读取python文件 fileName={} == 结束 ==" + " " + fileName);

        String result = "";
        try{
            Object okResult = okFuture.get();
            String oResult = okResult.toString();
            Object errResult = errFuture.get();
            String eResult = errResult.toString();
            result = !oResult.isEmpty() ? oResult : eResult;
        }catch (Exception e){
            result = "error:" + e.getMessage();
        }
        return result;
    }
    /**
     * 执行Blast分析 【同步 会等待py执行完毕】
     *
     * @param sense 待分析的基因序列
     * @param fileName blast使用的输入输出文件地址
     * @param params  blast命令字符串
     */
    public static String execBlastFileSync(String sense, String fileName, String taxo_id, String params) {
        String result = "";
        try {
            result = execBlastSync(sense, fileName, taxo_id, params);
            return result ;
        } catch (Exception e) {
            result = "error:" + e.getMessage();
        }
        return result;
    }

    private static String execBlastSync(String sense, String fileName, String taxo_id, String params) throws IOException {

        String thePath = "";
        if (OS.startsWith("Windows")) {
            thePath = WINDOWS_PATH;
        }else{
            thePath = LINUX_PATH;
        }

        String inFileStr = thePath + "blast/in/" + fileName + ".fasta";
        Path inFile = Paths.get(inFileStr);
        if (Files.exists(inFile)) {
            System.out.println("File already exists.");
        } else {
            Files.createFile(inFile);
            try (BufferedWriter out = new BufferedWriter(new FileWriter(inFile.toString()))) {
                out.write(">" + fileName);
                out.newLine();
                out.write(sense);
            } catch (IOException e) {
                System.out.println("File Write Error");
            }
        }

        String outFileStr = thePath + "blast/out/" + fileName + ".txt";
        Path outFile = Paths.get(outFileStr);
        if (Files.exists(outFile)) {
            System.out.println("File already exists.");
        }

        String dbPath = thePath + "blast/db/" + taxo_id;

        Process process;
        if (OS.startsWith("Windows")) {
            params = params.replaceAll("%infile%", inFileStr);
            params = params.replaceAll("%outfile%", outFileStr);
            params = params.replaceAll("%path%", thePath);
            process = new ProcessBuilder("cmd.exe", "/c", "blastn " + params).start();
        } else {
            List commend = new java.util.ArrayList();
            commend.add("blastn");
            commend.add("-task");
            commend.add("blastn");
            commend.add("-query");
            commend.add(inFileStr);
            commend.add("-db");
            commend.add(dbPath);
            commend.add("-out");
            commend.add(outFileStr);
            commend.add("-evalue");
            commend.add("1000");
            commend.add("-sorthits");
            commend.add("4");
            commend.add("-outfmt");
            commend.add("2");
            commend.add("-qcov_hsp_perc");
            commend.add("70");
            process = new ProcessBuilder(commend).start();
        }

        Future<Object> errFuture = taskPool.submit(() -> {
            //System.out.println("读取python文件 开始 fileName={}" + " " + fileName);
            BufferedReader errReader = null;
            // 脚本执行异常时的输出信息
            errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errStringList = read(fileName, errReader);
            //System.out.println("读取python文件 异常 fileName={}&errorString={}" + " " + fileName + " " + errorString);
            if(errStringList.size()>0){
                return errStringList.get(0);
            }else{
                return "";
            }
        });

        Future<Object> okFuture = taskPool.submit(() -> {
            // 脚本执行正常时的输出信息
            BufferedReader okReader = null;
            okReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> okStringList = read(fileName, okReader);
            //System.out.println("读取python文件 fileName={}&returnString={}" + " " + fileName + " " + returnString);
            if(okStringList.size()>0){
                return okStringList.get(0);
            }else{
                return "";
            }
        });

        try {
            //System.out.println("同步读取python文件 wait fileName={}" + " " + fileName);
            process.waitFor();
        } catch (InterruptedException e) {
            //System.out.println("同步读取python文件 fileName=" + " " + fileName + " " + " 等待结果返回异常" + " " + e.getMessage());
        }
        //System.out.println("同步读取python文件 fileName={} == 结束 ==" + " " + fileName);

        String cmdResult = "";
        JSONObject result = new JSONObject();
        result.put("errMsg", "");
        result.put("outFileName", outFile);
        try{
            Object okResult = okFuture.get();
            String oResult = okResult.toString();
            Object errResult = errFuture.get();
            String eResult = errResult.toString();
            cmdResult = oResult != "" ? oResult : eResult;
            result.put("errMsg", cmdResult);
        }catch (Exception e){
            result.put("errMsg", "error:" + e.getMessage());
        }
        return result.toString();
    }


}
